package main;

import graphics.GameWindow;

import graphics.OldGameWindow;


import graphics.MaxGraphics;
import io.LevelLoader;
import io.Network;
import io.ShipInputListener;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import util.LinAlg;
import util.Partition;
import util.Point;
import util.RunThread;
import util.Timer;

import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;

import objects.Asteroid;
import objects.AsteroidForceField;
import objects.Nebula;

import animation.ExplodingShip;

import secretweapons.Pulse;
import ship.Ship;
import ship.ShipConstants;
import structure.Incremental;
import structure.SObject;
import util.OldPartition;

public class Game implements Incremental {

	/**The game **/
	public static Game game;

	/** Window **/

	private GameWindow window;

	/**Lists of things: **/
	private Vector<SObject> emitGrav = new Vector<SObject>();
	private Vector<SObject> receiveGrav = new Vector<SObject>();
	private Vector<SObject> draw = new Vector<SObject>();
	private Vector<SObject> move = new Vector<SObject>();
	private Vector<SObject> collide = new Vector<SObject>();
	

	private Ship ship;
	private Vector<Ship> otherShips = new Vector<Ship>();

	/** Input and output **/

	private Network network;
	private ShipInputListener input;

	/** Current id **/
	public static int id = 0;

	/** Player we are currently controlling **/

	private int player;

	/** Graphics stuff **/

	private BufferedImage static1, static2;
	private ImageIcon static1Icon, static2Icon;
	private Timer staticTimer = new Timer(60, true);
	private BufferedImage buffer = new BufferedImage(OldGameWindow.WINDOW_X - (OldGameWindow.WINDOW_X - 1096), OldGameWindow.WINDOW_Y, BufferedImage.TYPE_INT_BGR);

	/** Thread **/

	RunThread run;
	
	/** Misc **/
	
	Point target = null;

	public Game(int player, Network n) {
		this.network = n;
		this.player = player;
		setup();
	}

	public void setup() {
		Game.game = this;

		try {
			static1 = ImageIO.read(new File("images/static1.png"));
			static2 = ImageIO.read(new File("images/static2.png"));
			static1Icon = new ImageIcon("images/static1.png");
			static2Icon = new ImageIcon("images/static2.png");
		} catch (Exception e) {
			e.printStackTrace();
		}
		staticTimer.start();

		LevelLoader.loadLevel("data/level.txt");
		AsteroidForceField a1 = new AsteroidForceField(new Point(260, 260), 800);
		a1.acc(new Point(1, 1));
		registerAll(a1);
		//Asteroid a = new Asteroid(new Point(400, 260), 4, 3);
		//a.acc(new Point(1, -1));
		//registerAll(a);
		Shape[] s = { new Rectangle(0, 0, 500, 10) };
		Nebula n = new Nebula(new Point(0, 0), s);
		registerAll(n);

		registerAll(new AsteroidForceField(new Point(700, 700), 800));
		
		for(int i=0; i < 10; ++i) {
			Asteroid a = new Asteroid(new Point(250, 250), 4, 3);
			a.acc(new Point((Math.random() - 0.5)*2, (Math.random() - 0.5)*2));
			registerAll(a);
		}


		window = new GameWindow();
		window.begin();
		input = new ShipInputListener();
		run = new RunThread(this);
		run.start();
	}

	public void increment() {
		input.update();
		update();
		collide();
		send();
		draw();
	}

	public void update() {

		for(int i = 0; i < move.size(); i++) {
			SObject o = move.get(i);
			if(o.isFlagged()) {
				move.remove(o);
				i--;
			}
			else {
				o.update();
			}
		}


	}

	/**
	 * Networking
	 */

	public void send() {
		for(Ship s : otherShips) { 
			
			for(SObject o : draw) {
				network.sendMessage(o.toString());
			}
		}
		network.sendMessage("Sync");
	}

	public void recieveMessage(String s) {
		input.recieveMessage(s);	
	}

	/**
	 * Checks collisions, is called by increment
	 */

	public void collide() {
	//	System.out.println("Here I am!");


		for(int i =0; i < collide.size(); i++)  {
			for(int j = 0; j < collide.size(); j++) {
				//System.out.println("Checking: " + collide.get(i).getClass() + " against " + collide.get(j).getClass());
				if(i != j && collide.get(i).getCords().distance(collide.get(j).getCords()) <
						collide.get(i).getRadius() + collide.get(j).getRadius()
						&& LinAlg.checkCollide(collide.get(i).getShape(), collide.get(j).getShape())) {
					//System.out.println("Collision");
					collide.get(i).collide(collide.get(j));
				}
			}
		}
		for(int i = 0; i < collide.size(); i++) {
			SObject s = collide.get(i);
			if(s.isFlagged()) {
				this.collide.remove(s);
			}
		}
	}


	/** Draw methods **/

	public void draw() {
		MaxGraphics g = new MaxGraphics((Graphics2D) buffer.getGraphics());
		
		drawStatic(g);
		


		int width = window.getWidth();
		int height = window.getHeight();
		g.translate((-ship.getX() + (width/2) - 150), (-ship.getY() + (height/2)));	

	
		
		g.setColor(Color.black);
		g.fillOval(ship.getX(), ship.getY(), 10000, 10000);

		if(target != null) {
			g.setColor(Color.red);
			g.drawLine(target.getX() -5, target.getY(), target.getX() + 5, target.getY());
			g.drawLine(target.getX() , target.getY() -5, target.getX(), target.getY() + 5);
		}

		for(int i = 0; i < draw.size(); i++) {
			SObject d = draw.get(i);
			if(d.isFlagged()) {
				this.draw.remove(d);
				i--;
			}
			else {
				d.draw(g);
			}
		}
		g.translate(-(-ship.getX() + (width/2) - 150), -(-ship.getY() + (height/2)));
		//panel.draw(g);
		window.getGraphicsForPainting().drawImage(buffer, 0, 0, null);
		window.update();
	}


	
	public void drawStatic(MaxGraphics g) {
		staticTimer.count();
		BufferedImage temp = null;
		ImageIcon tempIcon = null;
		if(staticTimer.getValue() > 30) {
			temp = static1;
			tempIcon = static1Icon;
		}
		else {
			temp = static2;
			tempIcon = static2Icon;
		}
		
		for(JLabel l : window.getStatic()) {
			l.setIcon(tempIcon);
			l.repaint();
		}
		for(int i = 0; i < 1500; i = i + 500) {
			for(int j = 0; j < 1500; j = j+500) {
				g.drawImage(temp, i, j);
			}
		}
	}


	/** Registration methods **/

	public void registerShip(Ship s, int reg) {
		if(s.getPlayer() == this.player) {
			this.ship = (Ship) s;
			otherShips.add(s);
		}
		else {
			otherShips.add(s);
		}
		registerObject(s, reg);
	}

	public void registerAll(SObject s) {
		registerObject(s, Constants.COLLIDE | Constants.DRAW | Constants.MOVE | Constants.EMIT_GRAV | Constants.RECIEVE_GRAV);
	}

	public void registerObject(SObject s, int reg) {

		if((reg & Constants.EMIT_GRAV) != 0) {
			emitGrav.add( s);
		}
		if((reg & Constants.RECIEVE_GRAV) != 0) {
			receiveGrav.add(s);
		}
		if((reg & Constants.MOVE) != 0) {
			this.move.add(s);
		}
		if((reg & Constants.DRAW) != 0) {
			this.draw.add(s);
		}
		if((reg & Constants.COLLIDE) != 0) {
			this.collide.add(s);
		}
	}


	public Ship getShip() {
		return ship;
	}
	
	public Vector<SObject> getMini() {
		return draw;
	}

	public GameWindow getWindow() {
		return window;
	}

	public Ship getOtherShip(int player) {
		for(Ship o : otherShips) {
			if(o.getPlayer() == player) {
				return o;
			}
		}
		return null;
	}

	public int getPlayer() {
		return player;
	}
	
	public void setTarget(Point p ) {
		int width = window.getWidth();
		int height = window.getHeight();
		target = p.translate(-(-ship.getX() + (width/2) - 150), -(-ship.getY() + (height/2)));
		System.out.println(target);
	}
	
	public Point getTarget() {
		return target;
	}

}
